package find.prj.lists;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public abstract class AbstractListClass<T> implements List<T>  {
	
	protected int Size;
	
	AbstractListClass() {
	}
	
	public int size() {
		return Size;
	}
	
	public boolean add(T value) {
		this.listIterator(Size).add(value);
		return true;
	}
	
	//method adds value before index. if index = 0, then the new value will be added as 1st element in the list
	public void add(int index, T value) {
		this.listIterator(index).add(value);
	}
	
	public boolean addAll(Collection<? extends T> c) {
		boolean result = false;
		ListIterator<T> iter = this.listIterator(Size);
		for (T value : c) {
			iter.add(value);
			result=true;
		}
		return result;
	}
	
	public boolean addAll(int index, Collection<? extends T> c) {
		boolean result = false;
		ListIterator<T> iter = this.listIterator(index);
		for (T value : c) {
			iter.add(value);
			result=true;
		}
		return result;
	}
	
	//Method implementation should be provided by subclasses
	public abstract void clear(); 

	public boolean contains(Object o) {
		for (T value : this)
			if (value.equals(o))
				return true;
		return false;
	}

	public boolean containsAll(Collection<?> c) {
		for (Object item : c) {
			if (! this.contains(item))
				return false;
		}
		return true;
	}

	public boolean equals(Object o) {
		throw new UnsupportedOperationException();
	}

	public T get(int index) {
		return this.listIterator(index).next();
	}

	public int indexOf(Object o) {
		throw new UnsupportedOperationException();
	}

	public boolean isEmpty() {
		if (Size == 0)
			return true;
		return false;
	}
	
	public Iterator<T> iterator() {
		return this.listIterator();
	}
	
	public int lastIndexOf(Object o) {
		throw new UnsupportedOperationException();
	}
 
	public ListIterator<T> listIterator() {
		return this.listIterator(0);
	}
	
	//Method implementation should be provided by subclasses
	public abstract ListIterator<T> listIterator(int index); 

	public T remove(int index) {
		ListIterator<T> iter = this.listIterator(index);
		T deletedValue = iter.next();
		iter.remove();
		return deletedValue;
	}

	public boolean remove(Object o) {
		throw new UnsupportedOperationException();
	}
	
	public boolean removeAll(Collection<?> c) {
		ListIterator<T> iter = this.listIterator();
		boolean isChanged = false;
		for (Object item : c) {
			while (iter.hasNext()) {
				T value = iter.next();
				if (value.equals(item)) {
					iter.remove();
					isChanged = true;
				}
			}
		}
		return isChanged;
	}

	public boolean retainAll(Collection<?> c) {
		ListIterator<T> iter = this.listIterator();
		boolean isChanged = false;
		for (Object item : c) {
			while (iter.hasNext()) {
				T value = iter.next();
				if (! value.equals(item)) {
					iter.remove();
					isChanged = true;
				}
			}
		}
		return isChanged;
	}

	public T set(int index, T value) {
		this.listIterator(index).set(value);
		return value;
	}

	//Method implementation should be provided by subclasses
	public abstract List<T> subList(int fromIndex, int toIndex);

	public Object[] toArray() {
		Object[] array = new Object[this.Size];
		ListIterator<T> iter = this.listIterator();
		int i = 0;
		while (iter.hasNext()) {
			array[i] = iter.next();
			i++;
		}
		return array;
	}

	@SuppressWarnings({ "unchecked", "hiding" })
	public <T> T[] toArray(T[] arg0) {
		if (arg0.length >= this.Size) {
			ListIterator<T> iter = (ListIterator<T>) this.listIterator();
			for (int i = 0; i < this.Size; i++)
				arg0[i] = iter.next();
			return null;
		}
		else
			return (T[])this.toArray();
	}
	
	public String toString() {
		StringBuilder result = new StringBuilder("");
		if(!this.isEmpty()) {
			for (T value : this)
				if (value == null)
					result.append("null ");
				else
					result.append(value.toString()+" ");
			result.replace(result.length()-1, result.length()-1, "");
		}
		return new String(result);
	}

	
}
